/**
图像处理库
**/
package imgo

import (
	"bytes"
	"encoding/base64"
	"errors"
	"image"
	"image/color"
	"image/jpeg"
	"image/png"
	"math"
	"os"
	"runtime"
)

//重采样滤波器
type resamplingFilter struct {
	Support float64
	Kernel  func(float64) float64
}

//读入图片返回[][][]uint8数据
func LoadImg(filepath string) (imgMatrix [][][]uint8) {
	img, decodeErr := DecodeImage(filepath)
	if decodeErr != nil {
		panic(decodeErr)
	}

	bounds := img.Bounds()
	width := bounds.Max.X
	height := bounds.Max.Y

	src := convertToNRGBA(img)
	imgMatrix = NewRGBAMatrix(height, width)

	for i := 0; i < height; i++ {
		for j := 0; j < width; j++ {
			c := src.At(j, i)
			r, g, b, a := c.RGBA()
			imgMatrix[i][j][0] = uint8(r)
			imgMatrix[i][j][1] = uint8(g)
			imgMatrix[i][j][2] = uint8(b)
			imgMatrix[i][j][3] = uint8(a)

		}
	}
	return
}

//读入图片返回image.Image
func DecodeImage(filePath string) (img image.Image, err error) {
	reader, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	img, _, err = image.Decode(reader)

	return
}

// 保存为png
func SaveAsPNG(filepath string, imgMatrix [][][]uint8) error {
	height := len(imgMatrix)
	width := len(imgMatrix[0])

	if height == 0 || width == 0 {
		return errors.New("The input of matrix is illegal!")
	}

	nrgba := image.NewNRGBA(image.Rect(0, 0, width, height))

	for i := 0; i < height; i++ {
		for j := 0; j < width; j++ {
			nrgba.SetNRGBA(j, i, color.NRGBA{imgMatrix[i][j][0], imgMatrix[i][j][1], imgMatrix[i][j][2], imgMatrix[i][j][3]})
		}
	}
	outfile, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer outfile.Close()

	png.Encode(outfile, nrgba)

	return nil
}

//保存为jpg
func SaveAsJPEG(filepath string, imgMatrix [][][]uint8, quality int) error {
	height := len(imgMatrix)
	width := len(imgMatrix[0])

	if height == 0 || width == 0 {
		return errors.New("The input of matrix is illegal!")
	}

	if quality < 1 {
		quality = 1
	} else if quality > 100 {
		quality = 100
	}

	nrgba := image.NewNRGBA(image.Rect(0, 0, width, height))

	for i := 0; i < height; i++ {
		for j := 0; j < width; j++ {
			nrgba.SetNRGBA(j, i, color.NRGBA{imgMatrix[i][j][0], imgMatrix[i][j][1], imgMatrix[i][j][2], imgMatrix[i][j][3]})
		}
	}
	outfile, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer outfile.Close()

	jpeg.Encode(outfile, nrgba, &jpeg.Options{Quality: quality})

	return nil
}

//三维数组到png数据
func ImgMatrixToBytes(imgMatrix [][][]uint8) ([]byte, error) {
	height := len(imgMatrix)
	width := len(imgMatrix[0])

	if height == 0 || width == 0 {
		return []byte{}, errors.New("The input of matrix is illegal!")
	}

	nrgba := image.NewNRGBA(image.Rect(0, 0, width, height))

	for i := 0; i < height; i++ {
		for j := 0; j < width; j++ {
			nrgba.SetNRGBA(j, i, color.NRGBA{imgMatrix[i][j][0], imgMatrix[i][j][1], imgMatrix[i][j][2], imgMatrix[i][j][3]})
		}
	}
	var b bytes.Buffer
	png.Encode(&b, nrgba)
	return b.Bytes(), nil
}

//将imgage转换成NRGBA
func convertToNRGBA(src image.Image) *image.NRGBA {
	srcBounds := src.Bounds()
	dstBounds := srcBounds.Sub(srcBounds.Min)

	dst := image.NewNRGBA(dstBounds)

	dstMinX := dstBounds.Min.X
	dstMinY := dstBounds.Min.Y

	srcMinX := srcBounds.Min.X
	srcMinY := srcBounds.Min.Y
	srcMaxX := srcBounds.Max.X
	srcMaxY := srcBounds.Max.Y

	switch src0 := src.(type) {

	case *image.NRGBA:
		rowSize := srcBounds.Dx() * 4
		numRows := srcBounds.Dy()

		i0 := dst.PixOffset(dstMinX, dstMinY)
		j0 := src0.PixOffset(srcMinX, srcMinY)

		di := dst.Stride
		dj := src0.Stride

		for row := 0; row < numRows; row++ {
			copy(dst.Pix[i0:i0+rowSize], src0.Pix[j0:j0+rowSize])
			i0 += di
			j0 += dj
		}

	case *image.NRGBA64:
		i0 := dst.PixOffset(dstMinX, dstMinY)
		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {

				j := src0.PixOffset(x, y)

				dst.Pix[i+0] = src0.Pix[j+0]
				dst.Pix[i+1] = src0.Pix[j+2]
				dst.Pix[i+2] = src0.Pix[j+4]
				dst.Pix[i+3] = src0.Pix[j+6]

			}
		}

	case *image.RGBA:
		i0 := dst.PixOffset(dstMinX, dstMinY)
		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {

				j := src0.PixOffset(x, y)
				a := src0.Pix[j+3]
				dst.Pix[i+3] = a

				switch a {
				case 0:
					dst.Pix[i+0] = 0
					dst.Pix[i+1] = 0
					dst.Pix[i+2] = 0
				case 0xff:
					dst.Pix[i+0] = src0.Pix[j+0]
					dst.Pix[i+1] = src0.Pix[j+1]
					dst.Pix[i+2] = src0.Pix[j+2]
				default:
					dst.Pix[i+0] = uint8(uint16(src0.Pix[j+0]) * 0xff / uint16(a))
					dst.Pix[i+1] = uint8(uint16(src0.Pix[j+1]) * 0xff / uint16(a))
					dst.Pix[i+2] = uint8(uint16(src0.Pix[j+2]) * 0xff / uint16(a))
				}
			}
		}

	case *image.RGBA64:
		i0 := dst.PixOffset(dstMinX, dstMinY)
		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {

				j := src0.PixOffset(x, y)
				a := src0.Pix[j+6]
				dst.Pix[i+3] = a

				switch a {
				case 0:
					dst.Pix[i+0] = 0
					dst.Pix[i+1] = 0
					dst.Pix[i+2] = 0
				case 0xff:
					dst.Pix[i+0] = src0.Pix[j+0]
					dst.Pix[i+1] = src0.Pix[j+2]
					dst.Pix[i+2] = src0.Pix[j+4]
				default:
					dst.Pix[i+0] = uint8(uint16(src0.Pix[j+0]) * 0xff / uint16(a))
					dst.Pix[i+1] = uint8(uint16(src0.Pix[j+2]) * 0xff / uint16(a))
					dst.Pix[i+2] = uint8(uint16(src0.Pix[j+4]) * 0xff / uint16(a))
				}
			}
		}

	case *image.Gray:
		i0 := dst.PixOffset(dstMinX, dstMinY)
		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {

				j := src0.PixOffset(x, y)
				c := src0.Pix[j]
				dst.Pix[i+0] = c
				dst.Pix[i+1] = c
				dst.Pix[i+2] = c
				dst.Pix[i+3] = 0xff

			}
		}

	case *image.Gray16:
		i0 := dst.PixOffset(dstMinX, dstMinY)
		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {

				j := src0.PixOffset(x, y)
				c := src0.Pix[j]
				dst.Pix[i+0] = c
				dst.Pix[i+1] = c
				dst.Pix[i+2] = c
				dst.Pix[i+3] = 0xff

			}
		}

	case *image.YCbCr:
		i0 := dst.PixOffset(dstMinX, dstMinY)
		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {

				yj := src0.YOffset(x, y)
				cj := src0.COffset(x, y)
				r, g, b := color.YCbCrToRGB(src0.Y[yj], src0.Cb[cj], src0.Cr[cj])

				dst.Pix[i+0] = r
				dst.Pix[i+1] = g
				dst.Pix[i+2] = b
				dst.Pix[i+3] = 0xff

			}
		}

	default:
		i0 := dst.PixOffset(dstMinX, dstMinY)
		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {

				c := color.NRGBAModel.Convert(src.At(x, y)).(color.NRGBA)

				dst.Pix[i+0] = c.R
				dst.Pix[i+1] = c.G
				dst.Pix[i+2] = c.B
				dst.Pix[i+3] = c.A

			}
		}
	}

	return dst
}

//生成[][][]uint8的数组
func New3DSlice(x int, y int, z int) (theSlice [][][]uint8) {
	theSlice = make([][][]uint8, x, x)
	for i := 0; i < x; i++ {
		s2 := make([][]uint8, y, y)
		for j := 0; j < y; j++ {
			s3 := make([]uint8, z, z)
			s2[j] = s3
		}
		theSlice[i] = s2
	}
	return
}

// 生成图片数组,4为rgba
func NewRGBAMatrix(height int, width int) (rgbaMatrix [][][]uint8) {
	rgbaMatrix = New3DSlice(height, width, 4)
	return
}

//调整图片长宽
func Resize(src *image.NRGBA, width int, height int) *image.NRGBA {
	dstW, dstH := width, height

	if dstW < 0 || dstH < 0 {
		return src
	}
	if dstW == 0 && dstH == 0 {
		return src
	}

	srcW := src.Rect.Max.X
	srcH := src.Rect.Max.Y

	if srcW <= 0 || srcH <= 0 {
		return src
	}

	// if new width or height is 0 then preserve aspect ratio, minimum 1px
	if dstW == 0 {
		tmpW := float64(dstH) * float64(srcW) / float64(srcH)
		dstW = int(math.Max(1.0, math.Floor(tmpW+0.5)))
	}
	if dstH == 0 {
		tmpH := float64(dstW) * float64(srcH) / float64(srcW)
		dstH = int(math.Max(1.0, math.Floor(tmpH+0.5)))
	}

	var dst *image.NRGBA

	var sinc = func(x float64) float64 {
		if x == 0 {
			return 1
		}
		return math.Sin(math.Pi*x) / (math.Pi * x)
	}

	var filter resamplingFilter = resamplingFilter{
		Support: 3.0,
		Kernel: func(x float64) float64 {
			x = math.Abs(x)
			if x < 3.0 {
				return sinc(x) * sinc(x/3.0)
			}
			return 0
		},
	}

	if filter.Support <= 0.0 {
		// nearest-neighbor special case
		dst = resizeNearest(src, dstW, dstH)

	} else {
		// two-pass resize
		if srcW != dstW {
			dst = resizeHorizontal(src, dstW, filter)
		} else {
			dst = src
		}

		if srcH != dstH {
			dst = resizeVertical(dst, dstH, filter)
		}
	}

	return dst
}

func resizeNearest(src *image.NRGBA, width, height int) *image.NRGBA {
	dstW, dstH := width, height

	srcBounds := src.Bounds()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()
	srcMinX := srcBounds.Min.X
	srcMinY := srcBounds.Min.Y
	srcMaxX := srcBounds.Max.X
	srcMaxY := srcBounds.Max.Y

	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))

	dx := float64(srcW) / float64(dstW)
	dy := float64(srcH) / float64(dstH)

	// divide image to parts for parallel processing
	numGoroutines := runtime.NumCPU()
	goMaxProcs := runtime.GOMAXPROCS(0)
	if numGoroutines > goMaxProcs {
		numGoroutines = goMaxProcs
	}
	if numGoroutines > dstH {
		numGoroutines = dstH
	}
	partSize := dstH / numGoroutines

	doneChan := make(chan bool, numGoroutines)

	for part := 0; part < numGoroutines; part++ {
		partStart := part * partSize
		partEnd := (part + 1) * partSize
		if part == numGoroutines-1 {
			partEnd = dstH
		}

		go func(partStart, partEnd int) {

			for dstY := partStart; dstY < partEnd; dstY++ {
				fy := float64(srcMinY) + (float64(dstY)+0.5)*dy - 0.5

				for dstX := 0; dstX < dstW; dstX++ {
					fx := float64(srcMinX) + (float64(dstX)+0.5)*dx - 0.5

					srcX := int(math.Min(math.Max(math.Floor(fx+0.5), float64(srcMinX)), float64(srcMaxX)))
					srcY := int(math.Min(math.Max(math.Floor(fy+0.5), float64(srcMinY)), float64(srcMaxY)))

					srcOffset := src.PixOffset(srcX, srcY)
					dstOffset := dst.PixOffset(dstX, dstY)

					dst.Pix[dstOffset+0] = src.Pix[srcOffset+0]
					dst.Pix[dstOffset+1] = src.Pix[srcOffset+1]
					dst.Pix[dstOffset+2] = src.Pix[srcOffset+2]
					dst.Pix[dstOffset+3] = src.Pix[srcOffset+3]
				}
			}

			doneChan <- true
		}(partStart, partEnd)
	}

	// wait for goroutines to finish
	for part := 0; part < numGoroutines; part++ {
		<-doneChan
	}

	return dst
}

//水平分布
func resizeHorizontal(src *image.NRGBA, width int, filter resamplingFilter) *image.NRGBA {
	srcBounds := src.Bounds()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()
	srcMinX := srcBounds.Min.X
	srcMinY := srcBounds.Min.Y
	srcMaxX := srcBounds.Max.X

	dstW := width
	dstH := srcH

	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))

	dX := float64(srcW) / float64(dstW)
	scaleX := math.Max(dX, 1.0)
	rX := math.Ceil(scaleX * filter.Support)

	// divide image to parts for parallel processing
	numGoroutines := runtime.NumCPU()
	goMaxProcs := runtime.GOMAXPROCS(0)
	if numGoroutines > goMaxProcs {
		numGoroutines = goMaxProcs
	}
	if numGoroutines > dstW {
		numGoroutines = dstW
	}
	partSize := dstW / numGoroutines

	doneChan := make(chan bool, numGoroutines)

	for part := 0; part < numGoroutines; part++ {
		partStart := part * partSize
		partEnd := (part + 1) * partSize
		if part == numGoroutines-1 {
			partEnd = dstW
		}

		go func(partStart, partEnd int) {

			for dstX := partStart; dstX < partEnd; dstX++ {
				fX := float64(srcMinX) + (float64(dstX)+0.5)*dX - 0.5

				startX := int(math.Ceil(fX - rX))
				if startX < srcMinX {
					startX = srcMinX
				}
				endX := int(math.Floor(fX + rX))
				if endX > srcMaxX-1 {
					endX = srcMaxX - 1
				}

				// cache weights
				weightSum := 0.0
				weights := make([]float64, int(rX+2)*2)
				for x := startX; x <= endX; x++ {
					w := filter.Kernel((float64(x) - fX) / scaleX)
					weightSum += w
					weights[x-startX] = w
				}

				for dstY := 0; dstY < dstH; dstY++ {
					srcY := srcMinY + dstY

					r, g, b, a := 0.0, 0.0, 0.0, 0.0
					for x := startX; x <= endX; x++ {
						weight := weights[x-startX]
						i := src.PixOffset(x, srcY)
						r += float64(src.Pix[i+0]) * weight
						g += float64(src.Pix[i+1]) * weight
						b += float64(src.Pix[i+2]) * weight
						a += float64(src.Pix[i+3]) * weight
					}

					r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
					g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
					b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
					a = math.Min(math.Max(a/weightSum, 0.0), 255.0)

					j := dst.PixOffset(dstX, dstY)
					dst.Pix[j+0] = uint8(r + 0.5)
					dst.Pix[j+1] = uint8(g + 0.5)
					dst.Pix[j+2] = uint8(b + 0.5)
					dst.Pix[j+3] = uint8(a + 0.5)
				}
			}

			doneChan <- true
		}(partStart, partEnd)

	}

	// wait for goroutines to finish
	for part := 0; part < numGoroutines; part++ {
		<-doneChan
	}

	return dst
}

//垂直调整
func resizeVertical(src *image.NRGBA, height int, filter resamplingFilter) *image.NRGBA {
	srcBounds := src.Bounds()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()
	srcMinX := srcBounds.Min.X
	srcMinY := srcBounds.Min.Y
	srcMaxY := srcBounds.Max.Y

	dstW := srcW
	dstH := height

	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))

	dY := float64(srcH) / float64(dstH)
	scaleY := math.Max(dY, 1.0)
	rY := math.Ceil(scaleY * filter.Support)

	// divide image to parts for parallel processing
	numGoroutines := runtime.NumCPU()
	goMaxProcs := runtime.GOMAXPROCS(0)
	if numGoroutines > goMaxProcs {
		numGoroutines = goMaxProcs
	}
	if numGoroutines > dstH {
		numGoroutines = dstH
	}
	partSize := dstH / numGoroutines

	doneChan := make(chan bool, numGoroutines)

	for part := 0; part < numGoroutines; part++ {
		partStart := part * partSize
		partEnd := (part + 1) * partSize
		if part == numGoroutines-1 {
			partEnd = dstH
		}

		go func(partStart, partEnd int) {

			for dstY := partStart; dstY < partEnd; dstY++ {
				fY := float64(srcMinY) + (float64(dstY)+0.5)*dY - 0.5

				startY := int(math.Ceil(fY - rY))
				if startY < srcMinY {
					startY = srcMinY
				}
				endY := int(math.Floor(fY + rY))
				if endY > srcMaxY-1 {
					endY = srcMaxY - 1
				}

				// cache weights
				weightSum := 0.0
				weights := make([]float64, int(rY+2)*2)
				for y := startY; y <= endY; y++ {
					w := filter.Kernel((float64(y) - fY) / scaleY)
					weightSum += w
					weights[y-startY] = w
				}

				for dstX := 0; dstX < dstW; dstX++ {
					srcX := srcMinX + dstX

					r, g, b, a := 0.0, 0.0, 0.0, 0.0
					for y := startY; y <= endY; y++ {
						weight := weights[y-startY]
						i := src.PixOffset(srcX, y)
						r += float64(src.Pix[i+0]) * weight
						g += float64(src.Pix[i+1]) * weight
						b += float64(src.Pix[i+2]) * weight
						a += float64(src.Pix[i+3]) * weight
					}

					r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
					g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
					b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
					a = math.Min(math.Max(a/weightSum, 0.0), 255.0)

					j := dst.PixOffset(dstX, dstY)
					dst.Pix[j+0] = uint8(r + 0.5)
					dst.Pix[j+1] = uint8(g + 0.5)
					dst.Pix[j+2] = uint8(b + 0.5)
					dst.Pix[j+3] = uint8(a + 0.5)
				}
			}

			doneChan <- true
		}(partStart, partEnd)

	}

	// wait for goroutines to finish
	for part := 0; part < numGoroutines; part++ {
		<-doneChan
	}

	return dst
}

//矩阵到向量
func Matrix2Vector(imgMatrix [][][]uint8) (vector []uint8) {
	h := len(imgMatrix)
	w := len(imgMatrix[0])
	r := len(imgMatrix[0][0])
	length := h * w * r

	vector = make([]uint8, length)

	for i := 0; i < h; i++ {
		for j := 0; j < w; j++ {
			for k := 0; k < r-1; k++ {
				vector = append(vector, imgMatrix[i][j][k])
			}
		}
	}
	return
}

//点
func Dot(x []uint8, y []uint8) float64 {
	xlen := len(x)

	var sum float64 = 0
	for i := 0; i < xlen; i++ {
		sum = sum + float64(x[i])*float64(y[i])

	}
	return sum
}

//旋转图片算法 任意角度
func Rotate(img [][][]uint8, ang float64) [][][]uint8 {
	rad := ang * (math.Pi / 180)
	nWidth := len(img[0])
	nHeight := len(img)
	SrcX1 := -nWidth / 2
	SrcY1 := nHeight / 2
	SrcX2 := nWidth / 2
	SrcY2 := nHeight / 2
	SrcX3 := nWidth / 2
	SrcY3 := -nHeight / 2
	SrcX4 := -nWidth / 2
	SrcY4 := -nHeight / 2

	DstX1 := int((math.Cos(rad)*float64(SrcX1) + math.Sin(rad)*float64(SrcY1)) + 0.5)
	DstY1 := int((-math.Sin(rad)*float64(SrcX1) + math.Cos(rad)*float64(SrcY1)) + 0.5)
	DstX2 := int((math.Cos(rad)*float64(SrcX2) + math.Sin(rad)*float64(SrcY2)) + 0.5)
	DstY2 := int((-math.Sin(rad)*float64(SrcX2) + math.Cos(rad)*float64(SrcY2)) + 0.5)
	DstX3 := int((math.Cos(rad)*float64(SrcX3) + math.Sin(rad)*float64(SrcY3)) + 0.5)
	DstY3 := int((-math.Sin(rad)*float64(SrcX3) + math.Cos(rad)*float64(SrcY3)) + 0.5)
	DstX4 := int((math.Cos(rad)*float64(SrcX4) + math.Sin(rad)*float64(SrcY4)) + 0.5)
	DstY4 := int((-math.Sin(rad)*float64(SrcX4) + math.Cos(rad)*float64(SrcY4)) + 0.5)

	DstWidth := int(math.Max(math.Abs(float64(DstX1-DstX3)), math.Abs(float64(DstX2-DstX4))) + 1.0)
	DstHeight := int(math.Max(math.Abs(float64(DstY1-DstY3)), math.Abs(float64(DstY2-DstY4))) + 1.0)

	out1 := New3DSlice(DstHeight, DstWidth, 4)

	VarX := float64(-float64(DstWidth)*math.Cos(rad)/2.0 - float64(DstHeight)*math.Sin(rad)/2.0 + float64(nWidth)/2.0)
	VarY := float64(float64(DstWidth)*math.Sin(rad)/2.0 - float64(DstHeight)*math.Cos(rad)/2.0 + float64(nHeight)/2.0)

	for i := 0; i < DstHeight; i++ { // i,j为现在的图的坐标
		for j := 0; j < DstWidth; j++ {
			x := int(math.Cos(rad)*float64(j) + math.Sin(rad)*float64(i) + VarX) //X，Y为原来图中的像素坐标
			y := int(-math.Sin(rad)*float64(j) + math.Cos(rad)*float64(i) + VarY)
			if x > nWidth-1 || x < 0 || y > nHeight-1 || y < 0 {
				out1[i][j][0] = 255
				out1[i][j][1] = 255
				out1[i][j][2] = 255
				out1[i][j][3] = 255
			} else {
				out1[i][j][0] = img[y][x][0]
				out1[i][j][1] = img[y][x][1]
				out1[i][j][2] = img[y][x][2]
				out1[i][j][3] = img[y][x][3]
			}
		}
	}
	return out1
}

//灰度
func RGB2Gray(src [][][]uint8) [][][]uint8 {
	height := len(src)
	width := len(src[0])
	imgMatrix := NewRGBAMatrix(height, width)
	copy(imgMatrix, src)

	for i := 0; i < height; i++ {
		for j := 0; j < width; j++ {
			avg := (imgMatrix[i][j][0] + imgMatrix[i][j][1] + imgMatrix[i][j][3]) / 3
			imgMatrix[i][j][0] = avg
			imgMatrix[i][j][1] = avg
			imgMatrix[i][j][2] = avg
			imgMatrix[i][j][3] = 255
		}
	}
	return imgMatrix
}

//二值化 将图片转成黑白
func Binaryzation(src [][][]uint8, reference int) [][][]uint8 {
	imgMatrix := src
	height := len(imgMatrix)
	width := len(imgMatrix[0])
	for i := 0; i < height; i++ {
		for j := 0; j < width; j++ {
			var rgb int = int(imgMatrix[i][j][0]) + int(imgMatrix[i][j][1]) + int(imgMatrix[i][j][2])
			if rgb > reference {
				rgb = 255
			} else {
				rgb = 0
			}
			imgMatrix[i][j][0] = uint8(rgb)
			imgMatrix[i][j][1] = uint8(rgb)
			imgMatrix[i][j][2] = uint8(rgb)
		}
	}

	return imgMatrix
}

//剪裁图片
func ClipImg(src [][][]uint8, x1, y1, x2, y2 int) [][][]uint8 {
	height := len(src)
	width := len(src[0])
	if x1 < 0 {
		x1 = 0
	}
	if y1 < 0 {
		y1 = 0
	}
	if x2 > width {
		x2 = width
	}
	if y2 > height {
		y2 = height
	}
	re := [][][]uint8{}
	//以上控制参数
	for i := y1; i < y2; i++ {
		line := [][]uint8{}
		for j := x1; j < x2; j++ {
			line = append(line, src[i][j])
		}
		re = append(re, line)
	}
	return re
}

//取向量值,base64字符串转向量值
func GetXiangLiang(pngstr string) (re []uint8) {
	data, err := base64.StdEncoding.DecodeString(pngstr)
	if err != nil {
		return
	}
	img, _, err := image.Decode(bytes.NewReader(data))
	if err != nil {
		return
	}
	nrgba := convertToNRGBA(img)
	src := Resize(nrgba, 80, 60)
	imgMatrix := NewRGBAMatrix(60, 80)
	for i := 0; i < 60; i++ {
		for j := 0; j < 80; j++ {
			c := src.At(j, i)
			r, g, b, a := c.RGBA()
			imgMatrix[i][j][0] = uint8(r)
			imgMatrix[i][j][1] = uint8(g)
			imgMatrix[i][j][2] = uint8(b)
			imgMatrix[i][j][3] = uint8(a)
		}
	}
	re = Matrix2Vector(imgMatrix)
	return
}

//去除上下空白
func QuiShx(src [][][]uint8) [][][]uint8 {
	width := len(src[0])
	ps := getHxwz(src)
	if len(ps) != 2 {
		return [][][]uint8{}
	}
	return ClipImg(src, 0, ps[0], width, ps[1])
}

//取出横线分割位置
func getHxwz(img [][][]uint8) []int {
	re := []int{0, 0}
	height := len(img)
	width := len(img[0])
	for i := 0; i < height; i++ {
		yhd := false
		for j := 0; j < width; j++ {
			if img[i][j][0] == 0 { //是黑点
				yhd = true
				break
			}
		}
		if yhd {
			re[0] = i
			break
		}

	}
	for i := height - 1; i >= 0; i-- {
		yhd := false
		for j := width - 1; j >= 0; j-- {
			if img[i][j][0] == 0 { //是黑点
				yhd = true
				break
			}
		}
		if yhd {
			if i == height {
				re[1] = height
			} else {
				re[1] = i
			}
			break
		}
	}
	return re
}

//纵向分割,reference为黑白分界值
func ZhxFg(img [][][]uint8, reference int) (re [][][][]uint8, ok bool) {
	img = Binaryzation(img, reference)
	height := len(img)
	ps := getSxwz(img)
	if len(ps)%2 == 0 {
		for i := 0; i < len(ps); i = i + 2 {
			k := ClipImg(img, ps[i], 0, ps[i+1], height)
			k = QuiShx(k)
			if len(k) < 1 {
				ok = false
				break
			} else {
				re = append(re, k)
			}
		}

	} else {
		ok = false
	}

	return
}

//取出竖线分割位置
func getSxwz(img [][][]uint8) []int {
	re := []int{}
	height := len(img)
	width := len(img[0])
	zhd := true
	for j := 0; j < width; j++ {
		if zhd { //找黑点
			yhd := false
			for i := 0; i < height; i++ {
				if img[i][j][0] == 0 { //是黑点
					yhd = true
					break
				}
			}
			if yhd {
				re = append(re, j)
				zhd = !zhd
			}

		} else {
			yhd := false
			for i := 0; i < height; i++ {
				if img[i][j][0] == 0 { //是黑点
					yhd = true
					break
				}
			}
			if !yhd {
				re = append(re, j)
				zhd = !zhd
			}
		}

	}

	return re
}

//比较两个向量的相似度
func XiangSiDu(myx, myy []uint8) float64 {
	cos1 := Dot(myx, myy)
	cos21 := math.Sqrt(Dot(myx, myx))
	cos22 := math.Sqrt(Dot(myy, myy))
	return cos1 / (cos21 * cos22)
}

//去噪点
func NaiveRemoveNoise(img [][][]uint8, pNum int) [][][]uint8 {
	img2 := img

	nWidth := len(img)
	nHeight := len(img[0])
	//set boundry to be white
	for i := 0; i < nWidth; i++ {
		img2[i][0] = []uint8{uint8(255), uint8(255), uint8(255), uint8(255)}
		img2[i][nHeight-1] = []uint8{uint8(255), uint8(255), uint8(255), uint8(255)}
	}
	for i := 0; i < nHeight; i++ {
		img2[0][i] = []uint8{uint8(255), uint8(255), uint8(255), uint8(255)}
		img2[nWidth-1][i] = []uint8{uint8(255), uint8(255), uint8(255), uint8(255)}
	}

	for j := 1; j < nHeight-1; j++ {
		for i := 1; i < nWidth-1; i++ {
			if img[i][j][0] == 0 { //黑点
				nCount := 0
				for m := i - 1; m <= i+1; m++ {
					for n := j - 1; n <= j+1; n++ {
						if img[m][n][0] == 0 {
							nCount++
						}
					}
				}
				if nCount <= pNum {
					img2[i][j] = []uint8{uint8(255), uint8(255), uint8(255), uint8(255)}
				}
			} else { //白点
				nCount := 0
				for m := i - 1; m <= i+1; m++ {
					for n := j - 1; n <= j+1; n++ {
						if img[m][n][0] == 0 {
							nCount++
						}
					}
				}
				if nCount > 7 {
					img2[i][j] = []uint8{uint8(0), uint8(0), uint8(0), uint8(255)}
				}
			}
		}
	}
	return img2
}

//去噪点方法2,中值滤波
func NaiveRemoveNoise2(img [][][]uint8) [][][]uint8 {
	img2 := img
	nWidth := len(img)
	nHeight := len(img[0])

	for j := 1; j < nHeight-1; j++ {
		for i := 1; i < nWidth-1; i++ {
			//取九点
			jiuD := [9][]uint8{}
			jiuD[0] = img[i-1][j-1]
			jiuD[1] = img[i][j-1]
			jiuD[2] = img[i+1][j-1]
			jiuD[3] = img[i-1][j]
			jiuD[4] = img[i][j]
			jiuD[5] = img[i+1][j]
			jiuD[6] = img[i-1][j+1]
			jiuD[7] = img[i][j+1]
			jiuD[8] = img[i+1][j+1]
			img2[i][j] = getZhong(jiuD)
		}
	}
	return img2
}

//去干扰线,功能有限
func RemoveLine(img [][][]uint8) [][][]uint8 {
	img2 := img
	nWidth := len(img)
	nHeight := len(img[0])

	for j := 1; j < nHeight-1; j++ {
		for i := 1; i < nWidth-1; i++ {
			if img[i][j][0] == 0 { //黑点
				flag := 0
				if img[i-1][j][0] == 255 && img[i+1][j][0] == 255 {
					flag++
				}
				if img[i][j-1][0] == 255 && img[i][j+1][0] == 255 {
					flag++
				}
				if img[i-1][j-1][0] == 255 && img[i+1][j+1][0] == 255 {
					flag++
				}
				if img[i-1][j+1][0] == 255 && img[i+1][j-1][0] == 255 {
					flag++
				}

				if flag > 0 {
					img2[i][j] = []uint8{uint8(0), uint8(0), uint8(0), uint8(255)}
				}
			}
		}
	}
	return img2
}

func getZhong(sjs [9][]uint8) []uint8 {
	for j := 0; j < 5; j++ {
		for i := j + 1; i < 9; i++ {
			if int(sjs[j][0])*100+int(sjs[j][1])*10+int(sjs[j][2]) > int(sjs[i][0])*100+int(sjs[i][1])*10+int(sjs[i][2]) {
				s := sjs[j]
				sjs[j] = sjs[i]
				sjs[i] = s
			}
		}
	}
	return sjs[4]
}
